/*! \file functions.c
 *  \brief Functions to manipulate list of functions. 
 *  \author Lluis Garrido
 *
 *  This file contains the structures necessary for
 *  manipulating the structure list_functions.
 */

#include "functions.h"

/*! 
 *  \brief Allocate a list_functions structure. 
 *
 *  \param[in] nitems  The maximum number of functions the list may hold. 
 *
 *  \return A pointer to the allocated structure. 
 *
 */

struct list_functions *list_functions_allocate(int nitems)
{
  int i;
  struct list_functions *l;

  assert(nitems > 0);

  l = (struct list_functions *)  malloc(sizeof(struct list_functions));

  if (!l)
  {
    printf("ERROR: not enough memory.\n");
    exit(EXIT_FAILURE);
  }

  l->el = (struct function *) malloc(sizeof(struct function) * nitems);

  if (!l->el)
  {
    printf("ERROR: not enough memory.\n");
    exit(EXIT_FAILURE);
  }

  l->nitems = nitems;
  l->currid = 1;

  for(i = 0; i < nitems; i++)
    l->el[i].function_id = FUNCTION_ID_NONE;

  return l;
}

/*! 
 *  \brief Evaluate numerically a function
 *
 *  \param[in] lfu          A pointer to the list of functions. 
 *  \param[in] function_id  The identifier of the functions (see ::list_functions_register_function1, ::list_functions_get_function_id)
 *  \param[in] arguments    The arguments to be passed to the function.
 *
 *  \return The numerical value associated to the variable. 
 *
 */

double list_functions_evaluate(struct list_functions *lfu, unsigned int function_id, double *arguments)
{
  double ret;
  int i;
  BOOL found = FALSE;

  for(i = 0; i < lfu->nitems; i++)
  {
    if (lfu->el[i].function_id == function_id)
    {
      found = TRUE;
      break;
    }
  }

  if (!found)
  {
    printf("ERROR: function is not defined.\n");
    exit(EXIT_FAILURE);
  }

  // Avoid complaining of compiler
  ret = 0.0;

  // We call here to the pointer of the function

  switch (lfu->el[i].nargs)
  {
    case 1:
      ret = (*(lfu->el[i].func.f1))(arguments[0]);
      break;

    case 2:
      ret = (*(lfu->el[i].func.f2))(arguments[0], arguments[1]);
      break;

    case 3:
      ret = (*(lfu->el[i].func.f3))(arguments[0], arguments[2], arguments[3]);
      break;

    default:
      printf("ERROR: invalid number of arguments.\n");
      break;
  }

  return ret;
}

/*! 
 *  \brief Get the identifier of the function. 
 *
 *  \param[in] lfu   A pointer to the list of functions. 
 *  \param[in] name  The name of the function. 
 *
 *  \return The identifier associated to the function. 
 *
 */

int list_functions_get_function_id(struct list_functions *lfu, char *name)
{
  BOOL found = FALSE;
  int i, ret = FUNCTION_ID_NONE;

  for(i = 0; i < lfu->nitems; i++)
  {
    if ((lfu->el[i].function_id != VARIABLE_ID_NONE) && (strncmp(name, lfu->el[i].name, MAX_SIZE_FUNCTION) == 0))
    {
      found = TRUE;
      break;
    }
  }

  if (found)
    ret = lfu->el[i].function_id;

  return ret;
}

/*! 
 *  \brief Get the name of a function. 
 *
 *  \param[in] lfu          A pointer to the list of functions. 
 *  \param[in] function_id  The identifier of the function. 
 *
 *  \return The name associated to the function. 
 *
 */

char *list_functions_get_name(struct list_functions *lfu, int function_id)
{
  int i;
  BOOL found = FALSE;

  for(i = 0; i < lfu->nitems; i++)
  {
    if (lfu->el[i].function_id == function_id)
    {
      found = TRUE;
      break;
    }
  }

  if (!found)
  {
    printf("ERROR: function is not defined.\n");
    exit(EXIT_FAILURE);
  }

  return lfu->el[i].name;
}

/*! 
 *  \brief Get the number of arguments associated to a function. 
 *
 *  \param[in] lfu          A pointer to the list of functions. 
 *  \param[in] function_id  The identifier of the function. 
 *
 *  \return The number of arguments associated to the function with function_id. 
 *
 */

int list_functions_get_arg_count(struct list_functions *lfu, int function_id)
{
  int i;
  BOOL found = FALSE;

  for(i = 0; i < lfu->nitems; i++)
  {
    if (lfu->el[i].function_id == function_id)
    {
      found = TRUE;
      break;
    }
  }

  if (!found)
  {
    printf("ERROR: function is not defined.\n");
    exit(EXIT_FAILURE);
  }

  return lfu->el[i].nargs;
}

/*! 
 *  \brief Gets the index to the first free available element
 *
 *  \param[in] lfu          A pointer to the list of functions. 
 *
 *  This function is used internally by ::list_functions_register_function1,
 *  ::list_functions_register_function2, ::list_functions_register_function3.
 *  There is no need to call this function from outside.
 *
 *  \return The index in list lfu that is free for registering a function.
 *
 */

int list_functions_get_first_free(struct list_functions *lfu)
{
  int i;
  BOOL found = FALSE;

  // Search for a free entry in the list

  for(i = 0; i < lfu->nitems; i++)
  {
    if (lfu->el[i].function_id == FUNCTION_ID_NONE)
    {
      // Free entry found

      found = TRUE;
      break;
    }
  }

  if (!found)
  {
    printf("ERROR: no more space to allocate function");
    exit(EXIT_FAILURE);
  }

  return i;
}

/*! 
 *  \brief Register a new function of one argument in the list of functions. 
 *
 *  \param[in] lfu     A pointer to the list of functions. 
 *  \param[in] name    The name of the function.
 *  \param[in] f1      A pointer to the function that evaluates the function.    
 *
 *  \return The identifier associated to the function. 
 *
 */

int list_functions_register_function1(struct list_functions *lfu, char *name, double (*f1)(double))
{
  int i, ret;

  // First check if the variable already exists
  ret = list_functions_get_function_id(lfu, name);

  // If the returned value is FUNCTION_ID_NONE the variable does 
  // not exist, otherwise 'ret' contains the id of the variable.
  if (ret != FUNCTION_ID_NONE)
  {
    printf("ERROR: function already registered.\n");
    exit(EXIT_FAILURE);
  }

  i = list_functions_get_first_free(lfu);

  lfu->el[i].function_id = lfu->currid;
  strncpy(lfu->el[i].name, name, MAX_SIZE_FUNCTION);
  lfu->el[i].nargs = 1;
  lfu->el[i].func.f1 = f1;

  ret = lfu->currid;
  lfu->currid++;

  return ret;
}

/*! 
 *  \brief Register a new function of two arguments in the list of functions. 
 *
 *  \param[in] lfu     A pointer to the list of functions. 
 *  \param[in] name    The name of the function.
 *  \param[in] f2      A pointer to the function that evaluates the function.    
 *
 *  \return The identifier associated to the function. 
 *
 */


int list_functions_register_function2(struct list_functions *lfu, char *name, double (*f2)(double, double))
{
  int i, ret;

  // First check if the variable already exists
  ret = list_functions_get_function_id(lfu, name);

  // If the returned value is FUNCTION_ID_NONE the variable does 
  // not exist, otherwise 'ret' contains the id of the variable.
  if (ret != FUNCTION_ID_NONE)
  {
    printf("ERROR: function already registered.\n");
    exit(EXIT_FAILURE);
  }

  i = list_functions_get_first_free(lfu);

  lfu->el[i].function_id = lfu->currid;
  strncpy(lfu->el[i].name, name, MAX_SIZE_FUNCTION);
  lfu->el[i].nargs = 2;
  lfu->el[i].func.f2 = f2;

  ret = lfu->currid;
  lfu->currid++;

  return ret;
}

/*! 
 *  \brief Register a new function of three arguments in the list of functions. 
 *
 *  \param[in] lfu     A pointer to the list of functions. 
 *  \param[in] name    The name of the function.
 *  \param[in] f3      A pointer to the function that evaluates the function.    
 *
 *  \return The identifier associated to the function. 
 *
 */

int list_functions_register_function3(struct list_functions *lfu, char *name, double (*f3)(double, double, double))
{
  int i, ret;

  // First check if the variable already exists
  ret = list_functions_get_function_id(lfu, name);

  // If the returned value is FUNCTION_ID_NONE the variable does 
  // not exist, otherwise 'ret' contains the id of the variable.
  if (ret != FUNCTION_ID_NONE)
  {
    printf("ERROR: function already registered.\n");
    exit(EXIT_FAILURE);
  }

  i = list_functions_get_first_free(lfu);

  lfu->el[i].function_id = lfu->currid;
  strncpy(lfu->el[i].name, name, MAX_SIZE_FUNCTION);
  lfu->el[i].nargs = 3;
  lfu->el[i].func.f3 = f3;

  ret = lfu->currid;
  lfu->currid++;

  return ret;
}

/*! 
 *  \brief An example of three argument function that is registered by ::list_functions_init_default 
 *
 *  \param[in] a    First parameter
 *  \param[in] b    Second parameter 
 *  \param[in] c    Third parameter
 *
 *  \return currently returns sqrt(a+b+c) 
 *
 */

double so2(double a, double b, double c)
{
  return sqrt(a+b+c);
}

/*! 
 *  \brief Insert default functions into the list. 
 *
 *  Inserts default functions (sin, cos, log, pow and ::so2) into the 
 *  list of functions.
 *
 *  \param[in] lfu     A pointer to the list of functions. 
 *
 */

void list_functions_init_default(struct list_functions *lfu)
{
  list_functions_register_function1(lfu, "Cos", cos);
  list_functions_register_function1(lfu, "Sin", sin);
  list_functions_register_function1(lfu, "Log", log);
  list_functions_register_function2(lfu, "Pow", pow);
  list_functions_register_function3(lfu, "So2", so2);
}


/*! 
 *  \brief Free a list_functions structure. 
 *
 *  \param[in] lfu  The list of functions to be freed.
 *
 */

void list_functions_free(struct list_functions *lfu)
{
  assert(lfu != NULL);
  assert(lfu->el != NULL);

  free(lfu->el);
  free(lfu);
}









